var zoom = d3.zoom()
width = window.innerWidth
height = window.innerHeight
margin = { top: 10, right: 120, bottom: 10, left: 40 }
dy = width / 6
dx = 10

// 树状图示例数据
fetch('flare-2.json')
  .then(resp => resp.json())
  .then(data => {
    tree = d3.tree().nodeSize([dx, dy])
    // 对角线
    diagonal = d3
      .linkHorizontal()
      .x(d => d.y)
      .y(d => d.x)

    // d3 处理 层级数据
    const root = d3.hierarchy(data)
    console.log('root', root)

    // 根节点位置
    root.x0 = dy / 2
    root.y0 = 0

    root.descendants().forEach((d, i) => {
      d.id = i
      d._children = d.children
      if (d.depth && d.data.name.length !== 7) d.children = null
    })

    // 创建svg元素
    const svg = d3
      .create('svg')
      .attr('viewBox', [-margin.left, -margin.top, width, dx])
      .style('font', '10px sans-serif')
      .style('user-select', 'none')
      .style('width', '100%')
      .style('height', '100%')

    // 在 svg 中 创建 g 元素
    const gLink = svg
      .append('g')
      .attr('fill', 'none')
      .attr('stroke', '#555') // 线条颜色
      .attr('stroke-opacity', 0.4) // 线条透明度
      .attr('stroke-width', 1.5) // 线条宽度

    const gNode = svg
      .append('g')
      .attr('cursor', 'pointer')
      .attr('pointer-events', 'all')

    // svg 调用缩放方法
    svg.call(
      d3
        .zoom()
        .extent([
          [0, 0],
          [width, height],
        ])
        .scaleExtent([1, 8]) // 缩放倍数
        .on('zoom', zoomed)
    )

    // 缩放方法
    function zoomed() {
      // 点和线同时缩放，不然会出现点或线保持原来大小
      gNode.attr('transform', d3.event.transform)
      gLink.attr('transform', d3.event.transform)
    }

    /**
     * @description: 更新节点
     * @param {Object} source=层级数据
     * @return:
     */
    function update(source) {
      const duration = d3.event && d3.event.altKey ? 2500 : 250
      const nodes = root.descendants().reverse()
      const links = root.links()

      // Compute the new tree layout.
      tree(root)

      let left = root
      let right = root
      root.eachBefore(node => {
        if (node.x < left.x) left = node
        if (node.x > right.x) right = node
      })

      const height = right.x - left.x + margin.top + margin.bottom

      const transition = svg
        .transition()
        .duration(duration)
        .attr('viewBox', [-margin.left, left.x - margin.top, width, height])
        .tween('resize', window.ResizeObserver ? null : () => () => svg.dispatch('toggle'))

      // Update the nodes…
      const node = gNode.selectAll('g').data(nodes, d => d.id)

      // Enter any new nodes at the parent's previous position.
      const nodeEnter = node
        .enter()
        .append('g')
        .attr('transform', d => `translate(${source.y0},${source.x0})`)
        .attr('fill-opacity', 0)
        .attr('stroke-opacity', 0)
        .on('click', d => {
          // 绑定点击展开和折叠子树
          d.children = d.children ? null : d._children
          update(d)
        })

      // 添加节点小圆点
      nodeEnter
        .append('circle')
        .attr('r', 2.5)
        .attr('fill', d => (d._children ? '#555' : '#999'))
        .attr('stroke-width', 10)

      // 添加节点文本
      nodeEnter
        .append('text')
        .attr('dy', '0.31em')
        .attr('x', d => (d._children ? -6 : 6))
        .attr('text-anchor', d => (d._children ? 'end' : 'start'))
        .text(d => d.data.name + (d.data.value || ''))
        .clone(true)
        .lower()
        .attr('stroke-linejoin', 'round')
        .attr('stroke-width', 3)
        .attr('stroke', 'white')

      // Transition nodes to their new position.
      const nodeUpdate = node
        .merge(nodeEnter)
        .transition(transition)
        .attr('transform', d => `translate(${d.y},${d.x})`)
        .attr('fill-opacity', 1)
        .attr('stroke-opacity', 1)

      // Transition exiting nodes to the parent's new position.
      const nodeExit = node
        .exit()
        .transition(transition)
        .remove()
        .attr('transform', d => `translate(${source.y},${source.x})`)
        .attr('fill-opacity', 0)
        .attr('stroke-opacity', 0)

      // Update the links…
      const link = gLink.selectAll('path').data(links, d => d.target.id)

      // Enter any new links at the parent's previous position.
      const linkEnter = link
        .enter()
        .append('path')
        .attr('d', d => {
          const o = { x: source.x0, y: source.y0 }
          return diagonal({ source: o, target: o })
        })

      // Transition links to their new position.
      link
        .merge(linkEnter)
        .transition(transition)
        .attr('d', diagonal)

      // Transition exiting nodes to the parent's new position.
      link
        .exit()
        .transition(transition)
        .remove()
        .attr('d', d => {
          const o = { x: source.x, y: source.y }
          return diagonal({ source: o, target: o })
        })

      // Stash the old positions for transition.
      root.eachBefore(d => {
        d.x0 = d.x
        d.y0 = d.y
      })
    }

    update(root)

    document.querySelector('#tree').append(svg.node())
  })
